package com.by.auth;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.by.auth.reamls.MyShiroJdbcRealmAdmin;
import com.by.auth.reamls.MyShiroJdbcRealmSuper;
import com.by.auth.reamls.MyShiroJdbcRealmUser;
import com.by.envi.WindowsCondition;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * shiro与spring整合
 */
@Configuration
@Conditional(WindowsCondition.class)
@SuppressWarnings("all")
public class ShiroBeansConfig {

	/**
	 * Shiro的Web过滤器Factory 命名:shiroFilter
	 * @param securityManager
	 * @return
	 */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

	    //添加自定义的过滤器
	    shiroFilterFactoryBean.setFilters(initMapFilter());

        shiroFilterFactoryBean.setFilterChainDefinitionMap(initUrlMapFilter());

        //如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");

        //登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //没有授权跳转的url
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/401");

        return shiroFilterFactoryBean;
    }


    /**
     * 多realm的情况下，认证策略配置
     * shiro提供了三种认证侧策略
     * 1：AtLeastOneSuccessfulStrategy
     * 2：AllSuccessfulStrategy
     * 3：FirstSuccessfulStrategy
     * @return
     */
    @Bean
    public ModularRealmAuthenticator authenticator() {
	    MyModularRealmAuthenticator modularRealmAuthenticator = new MyModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        return modularRealmAuthenticator;
    }

    @Bean
    public SessionIdGenerator sessionIdGenerator() {
		return new JavaUuidSessionIdGenerator();
    }



    /**
     * 自定义的realm
     * @return
     */
    @Bean
    public MyShiroJdbcRealmAdmin jdbcRealmAdmin() {
        MyShiroJdbcRealmAdmin jdbcRealmAdmin = new MyShiroJdbcRealmAdmin();

	    //禁用身份验证缓存，即缓存 AuthenticationInfo 信息，默认 false；
        //shiroJdbcRealm.setAuthenticationCachingEnabled(false);

	    //禁用授权缓存，使用自己的缓存逻辑；
	    //shiroJdbcRealm.setAuthorizationCachingEnabled(false);

	    //认证匹配器
        //shiroJdbcRealm.setCredentialsMatcher();

	    //使用缓存
	    //shiroJdbcRealm.setCachingEnabled(false);
        return jdbcRealmAdmin;
    }
	@Bean
	public MyShiroJdbcRealmUser jdbcRealmUser() {
		MyShiroJdbcRealmUser jdbcRealmUser = new MyShiroJdbcRealmUser();

		//禁用身份验证缓存，即缓存 AuthenticationInfo 信息，默认 false；
		//shiroJdbcRealm.setAuthenticationCachingEnabled(false);

		//禁用授权缓存，使用自己的缓存逻辑；
		//shiroJdbcRealm.setAuthorizationCachingEnabled(false);

		//认证匹配器
		//shiroJdbcRealm.setCredentialsMatcher();

		//使用缓存
		//shiroJdbcRealm.setCachingEnabled(false);
		return jdbcRealmUser;
	}
	@Bean
	public MyShiroJdbcRealmSuper jdbcRealmSuper() {
		MyShiroJdbcRealmSuper jdbcRealmSuper = new MyShiroJdbcRealmSuper();

		//禁用身份验证缓存，即缓存 AuthenticationInfo 信息，默认 false；
		//shiroJdbcRealm.setAuthenticationCachingEnabled(false);

		//禁用授权缓存，使用自己的缓存逻辑；
		//shiroJdbcRealm.setAuthorizationCachingEnabled(false);

		//认证匹配器
		//shiroJdbcRealm.setCredentialsMatcher();

		//使用缓存
		//shiroJdbcRealm.setCachingEnabled(false);

		return jdbcRealmSuper;
	}

    /**
     * 自定义的基于表单的身份验证过滤器
     * 基于FormAuthenticationFilter的实现
     * @return
     */
    @Bean
    public MyFormAuthenticationFilter formAuthenticationFilter() {
        MyFormAuthenticationFilter formAuthenticationFilter = new MyFormAuthenticationFilter();

        //认证的url地址
        formAuthenticationFilter.setLoginUrl("/login");

        //认证成功后跳转的url地址
        formAuthenticationFilter.setSuccessUrl("/index");

        //认证成功后每次都都跳转到SuccessUrl的地址
	    formAuthenticationFilter.setForceSuccessUrl(true);

	    //启用表示
        formAuthenticationFilter.setEnabled(true);

        return formAuthenticationFilter;
    }


	/**
	 * 配置shiro redisManager
	 *
	 * @return
	 */
	@Bean
	public RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost("127.0.0.1");
		redisManager.setPort(6379);
		redisManager.setTimeout(1800);
		redisManager.setDatabase(0);
		return redisManager;
	}

	/**
	 * cacheManager 缓存 redis实现
	 *
	 * @return
	 */
	@Bean
	public RedisCacheManager cacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager());
		//redisCacheManager.setKeyPrefix("users:security:authz:");
		redisCacheManager.setPrincipalIdFieldName("userId");
		//redisCacheManager.setKeySerializer();
		return redisCacheManager;
	}

	/**
	 * RedisSessionDAO shiro sessionDao层的实现 通过redis
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO () {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setRedisManager(redisManager());
		redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
		return redisSessionDAO;
	}

	/**
	 * shiro session的管理
	 */
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(redisSessionDAO());
		return sessionManager;
	}



    /**
     * Shiro安全管理器
     * 安全管理器使用用于web环境的 DefaultWebSecurityManager
     *
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager() {
    	DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    	//认证策略配置
    	securityManager.setAuthenticator(authenticator());
	    //多realm设置
    	Set<Realm> realmSet = new HashSet<Realm>();
	    realmSet.add(jdbcRealmSuper());
	    realmSet.add(jdbcRealmAdmin());
	    realmSet.add(jdbcRealmUser());
	    securityManager.setRealms(realmSet);

        //定义sessionmanager
        securityManager.setSessionManager(sessionManager());

	    //定义缓存实现类
	    securityManager.setCacheManager(cacheManager());

	    return securityManager;
    }

	/**
	 * 自定义扩展shiro权限标签
	 * @return
	 */
	@Bean
    public AnyRolesAuthorizationFilter anyRolesAuthorizationFilter() {
        AnyRolesAuthorizationFilter anyRolesAuthorizationFilter = new AnyRolesAuthorizationFilter();

        return anyRolesAuthorizationFilter;
    }

    /**
     * Shiro生命周期处理器
     * 保证实现了Shiro内部lifecycle函数的bean执行
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
	    LifecycleBeanPostProcessor postProcessor = new LifecycleBeanPostProcessor();
	    return postProcessor;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     *
     * AOP式方法级权限检查
     *
     * DefaultAdvisorAutoProxyCreator是用来扫描上下文，寻找所有的Advistor(通知器）将这些Advisor应用到所有符合切入点的Bean中。所以必须在lifecycleBeanPostProcessor创建之后创建，所以用了depends-on=”lifecycleBeanPostProcessor”>
     *
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator proxyCreator () {
	    DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
	    proxyCreator.setProxyTargetClass(true);
	    return proxyCreator;
    }

	/**
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor sourceAdvisor (@Qualifier("securityManager") SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		sourceAdvisor.setSecurityManager(securityManager);
		return sourceAdvisor;
	}
	/**
	 * thymeleaf 整合shiro标签库
	 * @return
	 */
	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	private Map<String, Filter> initMapFilter() {
		LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<String, Filter>();
		filterMap.put("anyRoles", anyRolesAuthorizationFilter());
		filterMap.put("authcA", formAuthenticationFilter());
		//filterMap.put("logout", logoutFilter());
		return filterMap;
	}

	private LinkedHashMap<String, String> initUrlMapFilter() {
		// 配置不会被拦截的链接 顺序判断

		//拦截器
		LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

		//跳转的中间页
		filterChainDefinitionMap.put("/", "anon");

		//配置不会被拦截的链接 顺序判断 -- start
		filterChainDefinitionMap.put("/resource/**", "anon");
		//验证码
		filterChainDefinitionMap.put("/code/image**", "anon");
		//登录
		filterChainDefinitionMap.put("/login", "anon");
		////配置不会被拦截的链接 顺序判断 -- end

		//配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
		filterChainDefinitionMap.put("/logout", "logout");

		//认证过的用户都能访问错误页面
		filterChainDefinitionMap.put("/error/**", "authcA");

		//系统后台主页
		filterChainDefinitionMap.put("/index", "authcA,anyRoles[ROLE_SUPER, ROLE_ADMIN, ROLE_USER]");

		//后台管理员特有的功能页面
		filterChainDefinitionMap.put("/super/**", "authcA, anyRoles[ROLE_SUPER]");

		filterChainDefinitionMap.put("/admin/**", "authcA, anyRoles[ROLE_SUPER,ROLE_ADMIN]");

		filterChainDefinitionMap.put("/user/**", "authcA, anyRoles[ROLE_SUPER, ROLE_USER]");


		// <!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
		// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
		filterChainDefinitionMap.put("/**", "authc");

		return filterChainDefinitionMap;
	}


	  /* @Bean
    @DependsOn("sessionIdGenerator")
    public EnterpriseCacheSessionDAO sessionDAO() {
	    EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();

	    //设置Session缓存名字，默认就是shiro-activeSessionCache
	    sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");

	    //用于生成会话ID，默认就是JavaUuidSessionIdGenerator，使用java.util.UUID生成
	    sessionDAO.setSessionIdGenerator(sessionIdGenerator());

	    return sessionDAO;
    }*/

	/**
	 * 用于生产SessionID Cookie的模板
	 */
    /*@Bean
    public SimpleCookie simpleCookie() {
	    SimpleCookie simpleCookie = new SimpleCookie();

	    //设置Cookie名字, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失
	    simpleCookie.setName("shiro.sesssion");

	    //设置Cookie的域名，默认空，即当前访问的域名
	    //simpleCookie.setDomain("");

	    //设置Cookie的路径，默认空，即存储在域名根下
	    simpleCookie.setPath("/");

	    //如果设置为true，则客户端不会暴露给客户端脚本代码，使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击；此特性需要实现了Servlet 2.5 MR6及以上版本的规范的Servlet容器支持
        simpleCookie.setHttpOnly(true);

        //设置Cookie的过期时间，秒为单位，默认-1表示关闭浏览器时过期Cookie
        simpleCookie.setMaxAge(1800);

        return simpleCookie;
    }*/


	/**
	 * 会话管理器使用用于web环境的用于web环境的 WebSessionManager DefaultWebSessionManager
	 * SessionManager会话管理器管理着应用中所有Subject的会话的创建、维护、删除、失效、验证等工作
	 *
	 * 在Servlet容器中，默认使用JSESSIONID Cookie维护会话，且会话默认是跟容器绑定的；在某些情况下可能需要使用自己的会话机制，此时我们可以使用DefaultWebSessionManager来维护会话
	 *
	 * @return
	 */
    /*@Bean
    @DependsOn("simpleCookie")
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

		//session的失效时长，单位毫秒
	    sessionManager.setGlobalSessionTimeout(180000);

	    //删除失效的session
	    sessionManager.setDeleteInvalidSessions(true);

	    //启用Cookie
        sessionManager.setSessionIdCookieEnabled(true);

        //配置Cookie
        sessionManager.setSessionIdCookie(simpleCookie());


        return sessionManager;
    }*/


	/**
	 * 注册过滤器
	 * @return
	 */
	/*@Bean
	public FilterRegistrationBean registrationBean (MyFormAuthenticationFilter formAuthenticationFilter) {
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		registrationBean.setFilter(formAuthenticationFilter);
		registrationBean.setEnabled(false);
		return registrationBean;
	}*/

}
